﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using BusinessObjects; 
using MainGameModule.ViewModels;
using YourWorld.Common.Infrastucture.Events;

namespace MainGameModule.Views
{
    public partial class GameMainView : UserControl
    {
        private DispatcherTimer timer;
        private GameMainViewModel viewModel;
        private Point scrollToPosition;
        private int ticks = 0;
        private const int MaxTimerTicks = 30;
        private const int ScrollOffset = 5;
        private double mapScale = 1;

        private Point lastDragPoint;
        private bool drag = false;

        private int sleep = Consts.SleepBetweenPlayerMoves;
        private DateTime lastMoveTime = DateTime.Now;

        private double minMapX;
        private double maxMapX = Consts.MapWidth;

        private double minMapY;
        private double maxMapY = Consts.MapHeight;

        public GameMainView( GameMainViewModel dataContext )
        {
            InitializeComponent();
            this.DataContext = dataContext;
            this.viewModel = dataContext;
            dataContext.InitializeGameModel();

            EventService.Aggregator.GetEvent<ScrollWithOffsetEvent>().Subscribe( this.ScrollWithOffset );
            EventService.Aggregator.GetEvent<PlayerChangedPositionEvent>().Subscribe( this.PlayerChangedPosition );
            EventService.Aggregator.GetEvent<ScaleGameMapEvent>().Subscribe( this.SetCurrentScale );

            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds( 1 );
            timer.Tick += new EventHandler( timer_Tick );

            this.AddHandler( UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler( MainPanel_MouseLeftButtonDown ), true );
            this.AddHandler( UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler( MainPanel_MouseLeftButtonUp ), true );

            this.AddHandler( UIElement.KeyDownEvent, new KeyEventHandler( MainPanel_KeyDown ), true );

            this.MouseMove += new MouseEventHandler( MainPanel_MouseMove );
            this.flickStoryboard.Completed += new EventHandler( ( o, a ) =>
            {
                this.ExperiencePopUp.Visibility = System.Windows.Visibility.Collapsed;
                EventService.Aggregator.GetEvent<AddToExperienceEventCompleted>().Publish( 0 );
            } );

            EventService.Aggregator.GetEvent<AddToExperienceEvent>().Subscribe( this.ShowExperiencePopUp );
            EventService.Aggregator.GetEvent<LevelFinished>().Subscribe( this.ShowLevelCompletedPopUp );
            EventService.Aggregator.GetEvent<ShowInfoBoxEvent>().Subscribe( this.ShowInfoGrid );
        }

        public void ShowInfoGrid( string payload )
        {
            this.InfoText.Text = payload;
            VisualStateManager.GoToState( this, "InfoState", true );
        }

        public void CancelDrag()
        {
            this.drag = false;
        }

        #region Mouse Drag
        private void MainPanel_KeyDown( object sender, KeyEventArgs e )
        {
            //if ( DateTime.Now.Subtract( this.lastMoveTime ).TotalMilliseconds < sleep )
            //    return;

            //this.lastMoveTime = DateTime.Now;
            //MoveDirection? direction = null;
            //switch ( e.Key )
            //{
            //    case Key.Left: direction = MoveDirection.Left; break;
            //    case Key.Right: direction = MoveDirection.Right; break;
            //    case Key.Up: direction = MoveDirection.Up; break;
            //    case Key.Down: direction = MoveDirection.Down; break;
            //}

            //if ( direction != null )
            //    EventService.Aggregator.GetEvent<MovePlayerEvent>().Publish( ( MoveDirection )direction );
        }

        private void MainPanel_MouseMove( object sender, MouseEventArgs e )
        {
            if ( drag )
            {
                Point curDragPoint = e.GetPosition( this );

                double x = ( curDragPoint.X - lastDragPoint.X );
                double y = ( curDragPoint.Y - lastDragPoint.Y );
                EventService.Aggregator.GetEvent<ScrollWithOffsetEvent>().Publish( new Point( x, y ) );

                lastDragPoint = curDragPoint;
            }
        }

        private void MainPanel_MouseLeftButtonDown( object sender, MouseButtonEventArgs e )
        {
            this.drag = true;
            this.lastDragPoint = e.GetPosition( this );
            this.Cursor = Cursors.Hand;
        }

        private void MainPanel_MouseLeftButtonUp( object sender, MouseButtonEventArgs e )
        {
            this.drag = false;
            this.Cursor = Cursors.Arrow;

            this.TestButton.Focus();
        }

        #endregion

        public void ShowLevelCompletedPopUp( int level )
        {
            this.Level.Text = level.ToString();
            this.ShowLevelFinishedPopUp.Begin();
        }

        public void ShowExperiencePopUp( int value )
        {
            //this.number.Text = value.ToString();
            if ( value > 0 )
            {
                this.flickStoryboard.Begin();
            }
        }

        public void SetCurrentScale( double scale )
        {
            this.mapScale = scale;

            double visibleWidth = Consts.MapWidth * scale;
            this.minMapX = ( Consts.MapWidth - visibleWidth ) / 2;
            this.maxMapX = Consts.MapWidth - this.minMapX - this.gameScrollviewer.ViewportWidth + 30;

            double visibleHeight = Consts.MapHeight * scale;
            this.minMapY = ( Consts.MapHeight - visibleHeight ) / 2;
            this.maxMapY = Consts.MapHeight - this.minMapY - this.gameScrollviewer.ViewportHeight;

            //double minX = this.gameScrollviewer.HorizontalOffset * this.mapScale;
            //double maxX = ( this.gameScrollviewer.HorizontalOffset + this.gameScrollviewer.ViewportWidth ) * this.mapScale;
            //double minY = this.gameScrollviewer.VerticalOffset * ( this.mapScale );
            //double maxY = ( this.gameScrollviewer.VerticalOffset + this.gameScrollviewer.ViewportHeight ) * ( this.mapScale );

            //Point scroll = new Point( 0, 0 );

            //if ( minX < this.minMapX )
            //    scroll.X = -this.minMapX + minX - 30;

            //if ( maxX > this.maxMapX )
            //    scroll.X = -this.maxMapX + maxX;

            //if ( minY < this.minMapY )
            //    scroll.Y = - this.minMapY + minY - 30;

            //if ( maxY > this.maxMapY )
            //    scroll.Y = +this.maxMapY - maxY;

            //if ( scroll.X != 0 || scroll.Y != 0 )
            //    this.ScrollWithOffset( scroll );
        }

        private void timer_Tick( object sender, EventArgs e )
        {
            if ( ticks >= MaxTimerTicks )
            {
                this.timer.Stop();
                return;
            }

            this.ScrollWithOffset( this.scrollToPosition );
            ticks++;
        }

        public void PlayerChangedPosition( Point pos )
        {
            double visibleHeight = Consts.MapHeight * this.mapScale;

            double myY = visibleHeight - pos.Y * this.mapScale;
            double myX = pos.X * this.mapScale;

            double minY = this.gameScrollviewer.VerticalOffset * ( this.mapScale );
            double maxY = ( this.gameScrollviewer.VerticalOffset + this.gameScrollviewer.ViewportHeight ) * ( this.mapScale );

            double minX = this.gameScrollviewer.HorizontalOffset * this.mapScale;
            double maxX = ( this.gameScrollviewer.HorizontalOffset + this.gameScrollviewer.ViewportWidth ) * this.mapScale;

            Point sctrollToPoint = new Point( -1, -1 );

            //Determine Y            
            if ( Math.Abs( maxY - myY ) < this.gameScrollviewer.ViewportHeight / 6 * this.mapScale )
                sctrollToPoint.Y = -ScrollOffset * this.mapScale;
            else if ( Math.Abs( minY - myY ) < this.gameScrollviewer.ViewportHeight / 6 * this.mapScale )
                sctrollToPoint.Y = ScrollOffset * this.mapScale;

            //Player not visible
            else if ( myY > maxY )
                sctrollToPoint.Y = ( ( maxY - myY - this.gameScrollviewer.ViewportHeight / 2 ) ) / MaxTimerTicks;
            else if ( myY < minY )
                sctrollToPoint.Y = ( ( minY - myY + this.gameScrollviewer.ViewportHeight / 2 ) ) / MaxTimerTicks;

            //Determine X
            if ( Math.Abs( maxX - myX ) < ( this.gameScrollviewer.ViewportWidth ) / 4 * this.mapScale )
                sctrollToPoint.X = -ScrollOffset * this.mapScale;
            else if ( Math.Abs( minX - myX ) < ( this.gameScrollviewer.ViewportWidth ) / 4 * this.mapScale )
                sctrollToPoint.X = ScrollOffset * this.mapScale;

            //Player not visible
            if ( myX > maxX )
                sctrollToPoint.X = ( maxX - myX - this.gameScrollviewer.ViewportWidth / 2 ) / MaxTimerTicks;
            else if ( myX < minX )
                sctrollToPoint.X = ( minX - myX + this.gameScrollviewer.ViewportWidth / 2 ) / MaxTimerTicks;

            if ( sctrollToPoint.X != -1 || sctrollToPoint.Y != -1 )
            {
                if ( sctrollToPoint.X == -1 )
                    sctrollToPoint.X = 0;
                else if ( sctrollToPoint.Y == -1 )
                    sctrollToPoint.Y = 0;

                this.ticks = 0;
                this.scrollToPosition = sctrollToPoint;
                this.timer.Start();
            }
        }

        public void ScrollWithOffset( Point point )
        {
            double newXPosition = this.gameScrollviewer.HorizontalOffset - point.X;
            double newYPosition = this.gameScrollviewer.VerticalOffset - point.Y;

            if ( this.minMapX < newXPosition && newXPosition < this.maxMapX )
                this.gameScrollviewer.ScrollToHorizontalOffset( newXPosition );

            if ( this.minMapY < newYPosition && newYPosition < this.maxMapY )
                this.gameScrollviewer.ScrollToVerticalOffset( newYPosition );

            this.gameScrollviewer.UpdateLayout();
        }

        private void mainGamePanel_KeyDown( object sender, KeyEventArgs e )
        {
            e.Handled = true;
        }

        private void gameScrollviewer_DragOver( object sender, DragEventArgs e )
        {

        }

        private void PhysicsKeyTrigger_KeyUp( object sender, EventArgs e )
        {

        }

        private void PhysicsKeyTrigger_KeyDown( object sender, EventArgs e )
        {

        }
    }
}